# Copyright (c) 2012 Nathan Grubb
# Available under the new BSD license; see LICENSE.txt

"""lib/plugin.py: Contains objects integral to the use of plugins, including
   the plugin API and the plugin loader.
"""

import traceback
import sys

class PluginApi(object):
    def __init__(self, grubbot):
        self.grubbot = grubbot

    def get_config_value(self, plugin, name):
        name = plugin.name + "_" + name

        if name in self.grubbot.config.values:
            return self.grubbot.config.values[name]

    def set_config_value(self, plugin, name, value):
        name = plugin.name + "_" + name
        self.grubbot.config.values[name] = value
        self.grubbot.config.save()

    def delete_config_value(self, plugin, name):
        name = plugin.name + "_" + name

        if name in self.grubbot.config.values:
            del self.grubbot.config.values[name]
            self.grubbot.config.save()
        
    def get_channels(self, nickname):
        """API function that returns all channels that a nickname is currently 
           in.
        """
        
        channels = []
        for channel, users in self.grubbot.users.items():
            if nickname in users:
                channels.append(channel)
        return channels

    def reply(self, msg, message):
        """Special quick-reply function for command plugins only (irc plugins shouldn't be
           sending messages anyway); assumes that msg.command is "privmsg"
        """

        import lib.message

        channel = msg.args[0]

        for line in message.splitlines():
            self.grubbot.connection.send("privmsg", channel, line)

            # HACK: Insert a fake PRIVMSG to trick the logger into logging this event
            # Fix for bug #1
            prefix = "%s!~%s@%s" % (self.grubbot.config.values["nickname"],
                                    self.grubbot.config.values["username"],
                                    "*")
            msg = str(lib.message.Message("privmsg", (channel, line), prefix=prefix))
            self.grubbot.connection.buffer += msg

    def extract_command(self, msg):
        line = " ".join(msg.args[1:])
        prefix = self.grubbot.config.values["prefix"]
        if line.startswith(prefix):
            line = line.lstrip(prefix)
            values = line.split()
            cmd = values[0]
            args = values[1:]

            if not cmd:
                return None
            else:
                return cmd, args

    def check_permissions(self, nickname, permissions):
        """Checks against the currently logged in sessions that the allowed
           person is logged in and has required permissions. Returns a boolean.
        """

        if nickname in self.grubbot.sessions:
            permissions_level = self.grubbot.sessions[nickname]
            if permissions == "op" and permissions_level in ("op", "admin"):
                return True
            elif permissions == "admin" and permissions == "admin":
                return True
            else:
                return False

class PluginLoader(object):
    """PluginLoader: Handles the loading and operation of plugins."""

    def __init__(self, grubbot):
        self.grubbot = grubbot
        self.api = PluginApi(grubbot)
        self.command_plugins = {} # Dict of command: plugin object
        self.irc_plugins = {} # Dict of IRC command: list of plugin objects

    def load(self):
        """Handles the loading of all plugins, also providing plugin debugging."""

        files = self.grubbot.config.values["plugins"]
        for plugin_name in files:
            try:
                plugin = getattr(__import__("plugins." + plugin_name),
                                 plugin_name).plugin
            except ImportError:
                self.grubbot.write_error(self, "Plugin %s is enabled but does not exist!" %(
                                                plugin_name))
            else:
                if plugin.type == "command":
                    for command in plugin.commands:
                        try:
                            getattr(plugin, "command_" + command)
                        except AttributeError:
                            self.grubbot.write_error(self, ("Plugin %s defines "
                                "command %s but assigns no behavior to it!" %\
                                 (plugin.name, command)))
                        else:
                            self.command_plugins[command] = plugin
                elif plugin.type == "irc":
                    for command in plugin.commands:
                        try:
                            getattr(plugin, "command_" + command)
                        except AttributeError:
                            self.grubbot.write_error(self, ("Plugin %s defines "
                                "command %s but assigns no behavior to it!" %\
                                 (plugin.name, command)))
                        else:
                            if command in self.irc_plugins:
                                self.irc_plugins[command].append(plugin)
                            else:
                                self.irc_plugins[command] = [plugin]

    def dispatch(self, msg):
        commandargs = self.api.extract_command(msg)
        if commandargs != None:
            command, args = commandargs
            if command in self.command_plugins:
                plugin = self.command_plugins[command]
                if plugin.commands[command] <= len(args):
                    if plugin.permissions == None\
                    or self.api.check_permissions(msg.nickname, plugin.permissions):
                        plugin = plugin(self.api, msg)
                        func = getattr(plugin, "command_" + command)
                        try:
                            func(args)
                        except Exception:
                            for line in traceback.format_exc().splitlines():
                                self.grubbot.write_error(plugin, line)
        if msg.command in self.irc_plugins:
            command = msg.command
            plugins = self.irc_plugins[command]
            for plugin in plugins:
                valid = False
                try:
                    nickname = getattr(msg, "nickname")
                except AttributeError:
                    valid = True
                else:
                    if plugin.permissions == None\
                    or self.api.check_permissions(msg.nickname, plugin.permissions):
                        valid = True
                if valid == True:
                    plugin = plugin(self.api, msg)
                    func = getattr(plugin, "command_" + command)
                    try:
                        func()
                    except Exception:
                        for line in traceback.format_exc().splitlines():
                            self.grubbot.write_error(plugin, line)